﻿using System;
using java = biz.ritter.javapi;
using biz.ritter.javapi.io;
using biz.ritter.javapi.lang;

namespace biz.ritter.javapi.util
{

    /*
     * UUID.java
     *
     * Created 07.02.2003
     *
     * eaio: UUID - an implementation of the UUID specification
     * Copyright (c) 2003-2009 Johann Burkard (jb@eaio.com) http://eaio.com.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included
     * in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
     * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
     * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     * USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    [Serializable()]
    public class UUID : IComparable, ICloneable
    {

        /**
         * Hasn't ever changed between versions.
         */
        const long serialVersionUID = 7435962790062944603L;

        /**
         * The time field of the UUID.
         *
         * @serial
         */
        public long time;

        /**
         * The clock sequence and node field of the UUID.
         *
         * @serial
         */
        public long clockSeqAndNode;

        private Guid guid;

        public UUID()
        {
            guid = new Guid();
            time = DateTime.UtcNow.Ticks;
            clockSeqAndNode = serialVersionUID;
        }

        public UUID(String s)
        {
            guid = new Guid(s);
            time = DateTime.UtcNow.Ticks;
            clockSeqAndNode = serialVersionUID;
        }

        /**
         * Constructor for UUID. Constructs a UUID from two <code>long</code> values.
         *
         * @param time the upper 64 bits
         * @param clockSeqAndNode the lower 64 bits
         */
        public UUID(long time, long clockSeqAndNode)
        {
            this.time = time;
            this.clockSeqAndNode = clockSeqAndNode;

            guid = Guid.NewGuid();
        }

        /**
         * Copy constructor for UUID. Values of the given UUID are copied.
         *
         * @param u the UUID, may not be <code>null</code>
         */
        public UUID(UUID u)
            : this(u.time, u.clockSeqAndNode)
        {
        }

        /**
         * Parses a textual representation of a UUID.
         * <p>
         * No validation is performed. If the {@link CharSequence} is shorter than 36 characters,
         * {@link ArrayIndexOutOfBoundsException}s will be thrown.
         *
         * @param s the {@link CharSequence}, may not be <code>null</code>
         */
        public UUID(CharSequence s)
            : this(Hex.parseLong(s.subSequence(0, 18)), Hex.parseLong(s.subSequence(19, 36)))
        {
        }

        public static UUID randomUUID()
        {
            return new UUID();
        }

        public int CompareTo(object obj)
        {
            return compareTo(obj as UUID);
        }
        
        /**
         * Compares this UUID to another Object. Throws a {@link ClassCastException} if
         * the other Object is not an instance of the UUID class. Returns a value
         * smaller than zero if the other UUID is "larger" than this UUID and a value
         * larger than zero if the other UUID is "smaller" than this UUID.
         *
         * @param t the other UUID, may not be <code>null</code>
         * @return a value &lt; 0, 0 or a value &gt; 0
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         * @throws ClassCastException
         */
        public int compareTo(UUID t)
        {
            if (this == t)
            {
                return 0;
            }
            if (time > t.time)
            {
                return 1;
            }
            if (time < t.time)
            {
                return -1;
            }
            if (clockSeqAndNode > t.clockSeqAndNode)
            {
                return 1;
            }
            if (clockSeqAndNode < t.clockSeqAndNode)
            {
                return -1;
            }
            return 0;
        }

        public static UUID fromString(String s)
        {
           return new UUID(s);
        }

        /**
         * Tweaked Serialization routine.
         *
         * @param out the ObjectOutputStream
         * @throws IOException
         */
        private void writeObject(ObjectOutputStream outStream) //throws IOException 
        {
            outStream.writeLong(time);
            outStream.writeLong(clockSeqAndNode);
        }

        /**
         * Tweaked Serialization routine.
         *
         * @param in the ObjectInputStream
         * @throws IOException
         */
        private void readObject(ObjectInputStream inStream) //throws IOException 
        {
            time = inStream.readLong();
            clockSeqAndNode = inStream.readLong();
        }

        /**
         * Returns this UUID as a String.
         *
         * @return a String, never <code>null</code>
         * @see java.lang.Object#toString()
         * @see #toAppendable(Appendable)
         */
        // @Override
        public String toString()
        {
            return toAppendable(null).toString();
        }

        /**
         * Appends a String representation of this to the given {@link StringBuffer} or
         * creates a new one if none is given.
         *
         * @param in the StringBuffer to append to, may be <code>null</code>
         * @return a StringBuffer, never <code>null</code>
         * @see #toAppendable(Appendable)
         */
        public StringBuffer toStringBuffer(StringBuffer inBuffer)
        {
            StringBuffer outBuffer = inBuffer;
            if (outBuffer == null)
            {
                outBuffer = new StringBuffer(36);
            }
            else
            {
                outBuffer.ensureCapacity(outBuffer.length() + 36);
            }
            return (StringBuffer)toAppendable(outBuffer);
        }

        /**
         * Appends a String representation of this object to the given {@link Appendable} object.
         * <p>
         * For reasons I'll probably never understand, Sun has decided to have a number of I/O classes implement
         * Appendable which forced them to destroy an otherwise nice and simple interface with {@link IOException}s.
         * <p>
         * I decided to ignore any possible IOExceptions in this method.
         *
         * @param a the Appendable object, may be <code>null</code>
         * @return an Appendable object, defaults to a {@link StringBuilder} if <code>a</code> is <code>null</code>
         */
        public Appendable toAppendable(Appendable a)
        {
            Appendable output = a;
            if (output == null)
            {
                output = new StringBuilder(36);
            }
            try
            {
                Hex.append(output, (int)(time >> 32)).append('-');
                Hex.append(output, (short)(time >> 16)).append('-');
                Hex.append(output, (short)time).append('-');
                Hex.append(output, (short)(clockSeqAndNode >> 48)).append('-');
                Hex.append(output, clockSeqAndNode, 12);
            }
            catch (IOException ex)
            {
                // What were they thinking?
            }
            return output;
        }

        /**
         * Returns a hash code of this UUID. The hash code is calculated by XOR'ing the
         * upper 32 bits of the time and clockSeqAndNode fields and the lower 32 bits of
         * the time and clockSeqAndNode fields.
         *
         * @return an <code>int</code> representing the hash code
         * @see java.lang.Object#hashCode()
         */
        //@Override
        public int hashCode()
        {
            return (int)((time >> 32) ^ time ^ (clockSeqAndNode >> 32) ^ clockSeqAndNode);
        }
        
        public Object Clone()
        {
            return clone();
        }
        /**
         * Clones this UUID.
         *
         * @return a new UUID with identical values, never <code>null</code>
         */
        //@Override
        public Object clone()
        {
            try
            {
                return MemberwiseClone();
            }
            catch (CloneNotSupportedException ex)
            {
                // One of Sun's most epic fails.
                return null;
            }
        }

        /**
         * Returns the time field of the UUID (upper 64 bits).
         *
         * @return the time field
         */
        public long getTime()
        {
            return time;
        }

        /**
         * Returns the clock and node field of the UUID (lower 64 bits).
         *
         * @return the clockSeqAndNode field
         */
        public long getClockSeqAndNode()
        {
            return clockSeqAndNode;
        }

        /**
         * Compares two Objects for equality.
         *
         * @see java.lang.Object#equals(Object)
         * @param obj the Object to compare this UUID with, may be <code>null</code>
         * @return <code>true</code> if the other Object is equal to this UUID,
         * <code>false</code> if not
         */
        //@Override
        public bool equals(Object obj)
        {
            if (!(obj is UUID))
            {
                return false;
            }
            return compareTo((UUID)obj) == 0;
        }

       
        /**
         * Returns the nil UUID (a UUID whose values are both set to zero).
         * <p>
         * Starting with version 2.0, this method does return a new UUID instance every
         * time it is called. Earlier versions returned one instance. This has now been
         * changed because this UUID has public, non-const instance fields. Returning a
         * new instance is therefore more safe.
         *
         * @return a nil UUID, never <code>null</code>
         */
        public static UUID nilUUID()
        {
            return new UUID(0, 0);
        }

    }
}
